今天的內容有些跟前幾天的類似,就當作是刻意練習吧。
進入新的頁面之後,幫昨天做好的元件新增一些功能。
datePicker.setDate(Date(), animated: true) // today
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(didTapSaveButton))
首先加上datePicker的初始值(當下),然後加上儲存的按鈕。當按下的時候會觸發didTapSaveButton
。
按下按鈕的時候,嘗試對DB進行寫入一筆新的transaction,其中的資料就是剛剛輸入的結果。Factory for a write Transaction. Essential object to create scope for updates.
@objc func didTapSaveButton() {
if let text = textField.text, !text.isEmpty {
let date = datePicker.date
realm.beginWrite()
let newItem = TodoListItem()
newItem.date = date
newItem.item = text
realm.add(newItem)
try! realm.commitWrite()
completionHandler?()
navigationController?.popToRootViewController(animated: true)
} else {
print("add something")
}
}
昨天好像沒有對hanlder
做足夠的描述,至少我自己還是一知半解。
`
Before we dive into a technical aspect, let’s talk about what it means to use completion handlers. Assume the user is updating an app while using it. You definitely want to notify the user when it is done. You possibly want to pop up a box that says,“Congratulations, now, you may fully enjoy!”
So, how do you run a block of code only after the download has been completed? Further, how do you animate certain objects only after a view controller has been moved to the next? Well, we are going to find out how to design one like a boss.
Based on my expansive vocabulary list, completion handlers stand for,
Do stuff when things have been done
`
語意上是區塊的完成,技術上來說,有一些方法在溝通時會需要特定權限、參數,使用handler可以知會該特定方法行為已經結束了。
public var completionHandler: (() -> Void)?
不過這邊沒有什麼參數要傳,因此很簡單這樣寫就可以了。
總之最後我們想要dismiss controller,回到最初的畫面,因此呼叫popToRootViewController
。
來到最後一個controller!
我們希望能看細節、刪除功能。
首先宣告這個item、刪除行為的handler、要放在畫面上的兩個元件,以及建立DB連線。
public var item: TodoListItem?
public var deletionHandler: (() -> Void)?
@IBOutlet var itemLabel: UILabel!
@IBOutlet var dateLabel: UILabel!
private var realm = try! Realm()
接著嘗試做日期parsing,我們希望把日期換成字串,在這邊使用static是由於date formatter預期會create a memory,那我們只會使用一次,不希望造成memory leak的現象。
static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
return dateFormatter
}()
loading的時候,把元件內容都一併load進來,並且新增刪除的按鈕,按下時會觸發didTapDelete
方法。
override func viewDidLoad() {
super.viewDidLoad()
itemLabel.text = item?.item
dateLabel.text = Self.dateFormatter.string(from: item!.date)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(didTapDelete))
}
這個方法跟先前的didTapSaveButton
類似,只是在資料庫的行為改成刪除。
然後也別忘記呼叫handler、回到主畫面。
@objc func didTapDelete() {
guard let item = self.item else {
return
}
realm.beginWrite()
realm.delete(item)
try! realm.commitWrite()
deletionHandler?()
navigationController?.popToRootViewController(animated: true)
}
一樣的,我們得把程式邏輯跟相對應的畫面bind起來。
首先加上class,指定為剛剛的viewViewControlller,並指定id。
接著加入兩個label,拖拉至畫面後分別加上constraints。
其中一個為上左右20、高70,另一個是上10、左右20、高70。
得到以下畫面之後右鍵(or兩指)點選,把程式中的IOutlet指定到相對應的UI元件。
最後,終於到了最後,我們回到主畫面程式ViewController。
點下某一個row的時候,呼叫instantiateViewController
使用指定好的id創造一個新的viewController,將一些變數放進去,再呼叫pushViewController
將該viewController放到receiver端stack中。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let item = data[indexPath.row]
guard let vc = storyboard?.instantiateViewController(identifier: "view") as? ViewViewController else {
return
}
vc.item = item
vc.deletionHandler = {[weak self] in
self?.refresh()
}
vc.navigationItem.largeTitleDisplayMode = .never
vc.title = item.item
navigationController?.pushViewController(vc, animated: true)
}
基本且陽春的to-do list就這麼做完了。
我在想是否有些功能與顯示能被優化,或是改變呈現方式。
或是我應該放下執念,前往別的專案了呢?
若有任何不足或有誤,歡迎指教 (゜▽゜;)